package com.kun.gulimall.guliproduct.controller;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.sts20150401.Client;
import com.aliyun.sts20150401.models.AssumeRoleRequest;
import com.aliyun.sts20150401.models.AssumeRoleResponse;
import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;

@RestController
@RequestMapping("/oss")
public class OssController {

    @Value("${alibaba.cloud.oss.role-arn}")
    public String roleArn;

    @Value("${alibaba.cloud.access-key}")
    public String access_key;

    @Value("${alibaba.cloud.secret-key}")
    public String accessKeySecret;

    @Value("${alibaba.cloud.oss.endpoint}")
    public String endpoint;
    String bucket = "gulimaill-hi";
    String region = "cn-beijing";

    String upload_dir = "dir";
    Long expire_time = 360L;

    /**
     * 初始化STS Client
     */
    public Client createStsClient() {
        Config config = new Config().setAccessKeyId(access_key).setAccessKeySecret(accessKeySecret);
        config.endpoint = endpoint;
        try {
            return new Client(config);
        } catch (Exception e) {
            throw new RuntimeException("初始化 STS 失败");
        }
    }

    /**
     * 获取STS临时凭证
     */
    public AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials getCredential() {
        Client client = createStsClient();
        AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest()
                .setRoleArn(roleArn).setRoleSessionName("yourRoleSessionName");// 自定义会话名称
        RuntimeOptions runtime = new RuntimeOptions();
        try {
            AssumeRoleResponse response = client.assumeRoleWithOptions(assumeRoleRequest, runtime);
            return response.body.credentials;
        } catch (TeaException error) {
            System.out.println(error.getMessage());
            System.out.println(error.getData().get("Recommend"));
            throw new RuntimeException(error.message);
        } catch (Exception error) {
            System.out.println(error.getMessage());
            throw new RuntimeException(error);
        }
    }

    @GetMapping("/get_post_signature_for_oss_upload")
    public ResponseEntity<Map<String, String>> getPostSignatureForOssUpload() throws Exception {
        AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials sts_data = getCredential();

        String accesskeyid = sts_data.accessKeyId;
        String accesskeysecret = sts_data.accessKeySecret;
        String securitytoken = sts_data.securityToken;

        //获取x-oss-credential里的date，当前日期，格式为yyyyMMdd
        TimeZone utc = TimeZone.getTimeZone("UTC");
        String date = DateUtil.format(DateUtil.date(System.currentTimeMillis()).setTimeZone(utc),
                "yyyyMMdd");
        //获取x-oss-date
        String x_oss_date = DateUtil.format(DateUtil.date(System.currentTimeMillis()).setTimeZone(utc),
                "yyyyMMdd'T'HHmmss'Z'");
        // 步骤1：创建policy。
        String x_oss_credential = accesskeyid + "/" + date + "/" + region + "/oss/aliyun_v4_request";

        // 步骤2：构造待签名字符串（StringToSign）。
        String stringToSign = getSigan(utc, securitytoken, x_oss_credential, x_oss_date);

        // 步骤3：计算SigningKey。
        byte[] dateKey = hmacsha256(("aliyun_v4" + accesskeysecret).getBytes(), date);
        byte[] dateRegionKey = hmacsha256(dateKey, region);
        byte[] dateRegionServiceKey = hmacsha256(dateRegionKey, "oss");
        byte[] signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request");
        // 步骤4：计算Signature。
        String signature = BinaryUtil.toHex(hmacsha256(signingKey, stringToSign));
        Map<String, String> response = new HashMap<>();
        // 将数据添加到 map 中
        response.put("x_oss_signature_version", "OSS4-HMAC-SHA256");
        response.put("policy", stringToSign);
        response.put("x_oss_credential", x_oss_credential);
        response.put("x_oss_date", x_oss_date);
        response.put("signature", signature);
        response.put("x_oss_security_token", securitytoken);
        response.put("dir", upload_dir);
        response.put("host", "http://" + bucket + ".oss-" + region + ".aliyuncs.com");
        // 返回带有状态码 200 (OK) 的 ResponseEntity，返回给Web端，进行PostObject操作
        return ResponseEntity.ok(response);
    }

    private String getSigan(TimeZone utc, String securitytoken, String x_oss_credential, String x_oss_date) {
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> policy = new HashMap<>();
        String expiration = DateUtil.offsetSecond(DateUtil.date(), expire_time.intValue())
                .setTimeZone(utc).setField(DateField.MILLISECOND, 0)
                .toString("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        policy.put("expiration", expiration);
        List<Object> conditions = new ArrayList<>();
        Map<String, String> bucketCondition = new HashMap<>();
        bucketCondition.put("bucket", bucket);
        conditions.add(bucketCondition);
        Map<String, String> securityTokenCondition = new HashMap<>();
        securityTokenCondition.put("x-oss-security-token", securitytoken);
        conditions.add(securityTokenCondition);

        Map<String, String> signatureVersionCondition = new HashMap<>();
        signatureVersionCondition.put("x-oss-signature-version", "OSS4-HMAC-SHA256");
        conditions.add(signatureVersionCondition);

        Map<String, String> credentialCondition = new HashMap<>();
        credentialCondition.put("x-oss-credential", x_oss_credential);
        conditions.add(credentialCondition);
        Map<String, String> dateCondition = new HashMap<>();
        dateCondition.put("x-oss-date", x_oss_date);
        conditions.add(dateCondition);
        conditions.add(Arrays.asList("content-length-range", 1, 10240000));
        conditions.add(Arrays.asList("eq", "$success_action_status", "200"));
        conditions.add(Arrays.asList("starts-with", "$key", upload_dir));
        policy.put("conditions", conditions);
        try {
            return new String(Base64.encodeBase64(mapper.writeValueAsString(policy).getBytes()));
        } catch (JsonProcessingException e) {
            throw new RuntimeException("转JSON失败");
        }
    }

    public static byte[] hmacsha256(byte[] key, String data) {
        try {
            // 初始化HMAC密钥规格，指定算法为HMAC-SHA256并使用提供的密钥。
            SecretKeySpec secretKeySpec = new SecretKeySpec(key, "HmacSHA256");
            // 获取Mac实例，并通过getInstance方法指定使用HMAC-SHA256算法。
            Mac mac = Mac.getInstance("HmacSHA256");
            // 使用密钥初始化Mac对象。
            mac.init(secretKeySpec);
            // 执行HMAC计算，通过doFinal方法接收需要计算的数据并返回计算结果的数组。
            return mac.doFinal(data.getBytes());
        } catch (Exception e) {
            throw new RuntimeException("Failed to calculate HMAC-SHA256", e);
        }
    }

}
